import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;


public class Sudoku extends JFrame {
	
	private class SudokuException extends Exception {
		private static final long serialVersionUID = 1L;
		
		SudokuException(String s) {
			super(s);
		}
		
	}

	private static final long serialVersionUID = 1L;
	
	int[] grid = new int[16];
	JTextField fields[] = new JTextField[16];
	
	Sudoku() {
		
		setTitle("4X4 Sudoku");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setSize(250, 300);
		setLocationRelativeTo(null);
		
		this.getContentPane().setLayout(new BorderLayout());
		JPanel buttonPanel = new JPanel(new GridLayout(4, 4));
		
		for (int i = 0; i < 16; i++) {
			fields[i] = new JTextField(1);
			fields[i].setHorizontalAlignment((int) JTextField.CENTER_ALIGNMENT);
			fields[i].addActionListener(new ActionListener() {
				
				public void actionPerformed(ActionEvent e) {
					for (int i = 0; i < 16; i++) {
						if (e.getSource() == fields[i]) {
							fieldActivated(i);
							break;
						}
					}
				}	
			});
			
			buttonPanel.add(fields[i]);
		}
		this.getContentPane().add(buttonPanel, BorderLayout.CENTER);
		JButton solveButton = new JButton("Lahenda!");
		solveButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (isLegitSudoku(grid, false))
					
					try {
						solve(grid);
					} catch (Exception e1) {
						JOptionPane.showMessageDialog(
								null, "Lahendus leitud!", "Sudoku", 
								JOptionPane.INFORMATION_MESSAGE, null);
						printThisGrid();
					}
				else
					JOptionPane.showMessageDialog(
						null, "Sisestatud andmetes leidub vigu!", "Sudoku", 
						JOptionPane.INFORMATION_MESSAGE, null);
			}
		});
		
		this.getContentPane().add(solveButton, BorderLayout.SOUTH);
		
		this.setVisible(true);
		JOptionPane.showMessageDialog(
				null, "Sisesta esialgne seis kirjutades lahtrisse numbreid ja vajutades ENTER", "Sudoku", 
				JOptionPane.INFORMATION_MESSAGE, null);
		
	}
	
	void fieldActivated(int atIndex) {
		int userInput = 0;
		
		while (userInput < 1 || userInput > 4) {
			try {
				String s = fields[atIndex].getText();
				userInput = Integer.parseInt(s);
				if (userInput < 1 || userInput > 4) {
					throw new Exception();
				}
			} catch (Exception e) {
				JOptionPane.showMessageDialog(
						null, "Vale väärtus!", "Sudoku", 
						JOptionPane.ERROR_MESSAGE);
				return;
			}
			
		}
		
		grid[atIndex] = userInput;
		fields[atIndex].setText(""+userInput);
		fields[atIndex].setEnabled(false);
		
	}
	
	static int[] getQuadret(int i) {
		switch (i) {
		case 0:
			return new int[] {0, 1, 4, 5};
		case 1:
			return new int[] {2, 3, 6, 7};
		case 2:
			return new int[] {8, 9, 12, 13};
		case 3:
			return new int[] {10, 11, 14, 15};
		}
		return null;
	}
	
	static boolean isLegitSudoku(int[] board, boolean complete) {
		
		/* lines */
		for (int i = 0; i < 16; i+=4) {
			// for each line
			for (int j = 1; j < 5; j++) {
				// for each 1..4 value
				int cnt=0;
				for (int k = 0; k < 4; k++) {
					if (board[i+k] == j) {
						cnt++;
					}
				}
				if (complete && cnt != 1 || cnt > 1)
					return false;
			}
		}
		
		/* columns */
		for (int i = 0; i < 4; i++) {
			// for each line
			for (int j = 1; j < 5; j++) {
				// for each 1..4 value
				int cnt=0;
				for (int k = 0; k < 16; k+=4) {
					if (board[i+k] == j) {
						cnt++;
					}
				}
				if (complete && cnt != 1 || cnt > 1)
					return false;
			}
		}
		
		/* quadrets */
		for (int i = 0; i < 4; i++) {
			// for each quad
			for (int j = 1; j < 5; j++) {
				// for each 1..4 value
				int[] quad = getQuadret(i);
				int cnt=0;
				for (int k : quad) {
					if (board[k] == j)
						cnt++;
				}
				if (complete && cnt != 1 || cnt > 1)
					return false;
			}
		}
		return true;
	}
	
	void solve(int[] grid) throws Exception {
		int[] myGrid = grid.clone();
		

		if (isLegitSudoku(myGrid, true)) {
			this.grid = myGrid;
			throw new SudokuException("FOUND SOLVE!");
			
		}
		
		int indexOfZero = -1;
		for (int i = 0; i < 16; i++) {
			if (myGrid[i] == 0) {
				indexOfZero = i;
				break;
			}
		}
		if (indexOfZero == -1)
			return;
		
		for (int i = 1; i < 5; i++) {
			// try all values into slot
			myGrid[indexOfZero] = i;
			if (isLegitSudoku(myGrid, false))
				solve(myGrid);
		}
	}
	
	void printThisGrid() {
		
		for (int i = 0; i < 16; i++) {
			fields[i].setText(""+grid[i]);
			fields[i].setEnabled(false);
		}
		
		this.repaint();
		
	}
	
	public static void main(String[] args) {
		new Sudoku();

	}

}
